package com.xbongbong.saas.help;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xbongbong.paas.constant.ConfigConstant;
import com.xbongbong.paas.constant.MessageConstant;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.constant.SizeConstant;
import com.xbongbong.paas.domain.entity.PaasFormExplainEntity;
import com.xbongbong.paas.domain.entity.ext.ConditionsEntityExt;
import com.xbongbong.paas.domain.entity.ext.PaasFormDataEntityExt;
import com.xbongbong.paas.elasticsearch.model.PaasFormDataEsModel;
import com.xbongbong.paas.enums.ConditionEnum;
import com.xbongbong.paas.enums.DelEnum;
import com.xbongbong.paas.enums.FieldTypeEnum;
import com.xbongbong.paas.enums.IndexTypeEnum;
import com.xbongbong.paas.enums.PaymentSheetTypeEnum;
import com.xbongbong.paas.enums.SheetTypeEnum;
import com.xbongbong.paas.field.FieldAttrEntity;
import com.xbongbong.paas.help.CommonHelp;
import com.xbongbong.paas.model.PaasFormExplainModel;
import com.xbongbong.paas.pojo.IdNamePojo;
import com.xbongbong.paas.pojo.dto.FormDataListDTO;
import com.xbongbong.paas.pojo.vo.PaasFormDataEsListVO;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.helper.PageHelper;
import com.xbongbong.paas.toolbox.util.Arith;
import com.xbongbong.paas.toolbox.util.BeanUtil;
import com.xbongbong.paas.util.ExplainUtil;
import com.xbongbong.pro.domain.entity.UpdateDataEntity;
import com.xbongbong.pro.enums.errorcodes.PaymentErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.SystemErrorCodeEnum;
import com.xbongbong.pro.service.toolbox.statistic.help.FormHelp;
import com.xbongbong.pro.sheet.pojo.AmountDetailGetPojo;
import com.xbongbong.pro.sheet.pojo.dto.SheetAmountDetailGetDTO;
import com.xbongbong.pro.sheet.pojo.vo.HandleLinkInfoVO;
import com.xbongbong.pro.sheet.pojo.vo.SheetAmountDetailGetVO;
import com.xbongbong.saas.constant.BasicConstant;
import com.xbongbong.saas.constant.ErrorMessageConstant;
import com.xbongbong.saas.domain.entity.ext.ContractEntityExt;
import com.xbongbong.saas.domain.entity.ext.PaymentEntityExt;
import com.xbongbong.saas.domain.entity.ext.PaymentSheetEntityExt;
import com.xbongbong.saas.enums.CompanyConfigEnum;
import com.xbongbong.saas.enums.RedundantTemplateTypeEnum;
import com.xbongbong.saas.enums.SheetAloneEnum;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import com.xbongbong.saas.enums.business.ContractEnum;
import com.xbongbong.saas.enums.business.PaymentEnum;
import com.xbongbong.saas.enums.business.PaymentSheetEnum;
import com.xbongbong.saas.enums.dictionary.HasRedEnum;
import com.xbongbong.saas.enums.dictionary.PaymentStatusEnum;
import com.xbongbong.saas.help.workflow.PayAndPaymentSheetHelp;
import com.xbongbong.saas.model.ContractModel;
import com.xbongbong.saas.model.PaymentModel;
import com.xbongbong.saas.model.PaymentSheetModel;
import com.xbongbong.saas.toolbox.help.FastJsonHelper;
import com.xbongbong.sys.domain.entity.CompanyConfigEntity;
import com.xbongbong.sys.model.CompanyConfigModel;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import static com.xbongbong.saas.enums.business.PaymentEnum.REAL_AMOUNT;

/**
 * @author: 汪腾伟
 * @date: Created in 2021/9/8 10:24
 * @description： 适配工作流这边的额应收款帮助类
 */
@Slf4j
@Component
public class SaasPaymentHelper {

    @Resource
    private PaymentSheetModel paymentSheetModel;
    @Resource
    private FundHelp fundHelp;
    @Resource
    private FormHelp formHelp;
    @Resource
    private SaasPaymentSheetHelper saasPaymentSheetHelper;
    @Resource
    private CommonHelp commonHelp;
    @Resource
    private PaasFormExplainModel paasFormExplainModel;
    @Resource
    private CompanyConfigModel companyConfigModel;
    @Resource
    private PaasFormDataEsModel paasFormDataEsModel;
    @Resource
    private PaymentModel paymentModel;
    @Resource
    private PayAndPaymentSheetHelp payAndPaymentSheetHelp;
    @Resource
    private ContractModel contractModel;


    /**
     *
     *
     * @param sumFlag  true代表只计算总额  false代表除了得到总额，还要处理明细
     * @param sheetAmountDetailGetDTO
     * @return
     */
    public SheetAmountDetailGetVO paymentAmountDetailGet(boolean sumFlag, SheetAmountDetailGetDTO sheetAmountDetailGetDTO) throws XbbException {
        Integer businessType = sheetAmountDetailGetDTO.getBusinessType();
        String corpid = sheetAmountDetailGetDTO.getCorpid();
        List<Long> dataIdList = sheetAmountDetailGetDTO.getDataIdList();
        PaymentSheetTypeEnum paymentSheetTypeEnum = PaymentSheetTypeEnum.getByBusinessType(businessType);
        boolean isRedFlag = PaymentSheetTypeEnum.isRed(paymentSheetTypeEnum.getCode());
        //默认读取应收款/合同的未收款，但是红冲已核销、红冲预收款核销时需要读取应收款/合同的已收（红冲预收款不会关联合同和应收款，不会调用该接口）
        PaymentEnum paymentEnum = PaymentEnum.UN_AMOUNT;
        ContractEnum contractEnum = ContractEnum.UN_FINISH_AMOUNT;
        if (isRedFlag) {
            paymentEnum = REAL_AMOUNT;
            contractEnum = ContractEnum.FINISH_AMOUNT;
        }
        if (sumFlag) {
            //只计算总额，不用封装明细，但是要从每条应收款内求和得到已收/未收总额
            return getResult4AmountDetailSum(isRedFlag, sheetAmountDetailGetDTO, dataIdList, paymentEnum, contractEnum, corpid);
        } else {
            //除了得到总额，还要处理明细-按顺序封装明细内所有东西：签订人、金额、合同名、应收款名等
            return getResult4AmountDetail(corpid, isRedFlag, sheetAmountDetailGetDTO, dataIdList, paymentEnum, contractEnum);
        }
    }

    /**
     * 只计算总的未收/已收金额即可，不用封装明细，但是要从每条应收款内求和得到已收/未收总额（回款单保存验证金额时，需要获取总金额进行数值校验）
     * @param isRedFlag 是否红冲，如果红冲则读取关联原始回款单相关（红冲预收款不会关联合同和应收款，不会调用该接口）
     * @param sheetAmountDetailGetDTO 获取回款单明细dto
     * @param dataIdList 合同id集合
     * @param paymentEnum 未收/实收字段
     * @param contractEnum 未收/已收字段
     * @param corpid 公司id
     * @return SheetAmountDetailGetVO
     * @throws XbbException
     * @author zcp
     * @date 19/12/11 011 21:23
     * @update 19/12/11 011 21:23
     * @since v1.0
     * @version v1.0
     */
    public SheetAmountDetailGetVO getResult4AmountDetailSum(boolean isRedFlag, SheetAmountDetailGetDTO sheetAmountDetailGetDTO, List<Long> dataIdList, PaymentEnum paymentEnum, ContractEnum contractEnum, String corpid) throws XbbException {
        SheetAmountDetailGetVO sheetAmountDetailGetVO = new SheetAmountDetailGetVO();
        //封装查询应收款（未收或已收）的条件，如果是红冲的，还需要获得关联的原始回款单
        AmountDetailGetPojo amountDetailGetPojo = getPaymentAmountDetailGetPojo(isRedFlag, sheetAmountDetailGetDTO);
        if (isRedFlag) {
            //TODO 需要这边把审批状态考虑进去
            double redAmount = saasPaymentSheetHelper.sumRedAmountBySheet(corpid, sheetAmountDetailGetDTO.getLinkPreId());
            amountDetailGetPojo.setRedAmount(redAmount);
            fundHelp.subRedAmount(XbbRefTypeEnum.PAYMENT_SHEET, sheetAmountDetailGetVO, amountDetailGetPojo);
        } else {
            // 得到应收款（未收/已收）查询结果：只求总的未收/已收，则无需排序，因此explainMap、sortMap都传null
            List<PaasFormDataEntityExt> planList = getPlanList4AmountDetailGet(amountDetailGetPojo, sheetAmountDetailGetDTO, null, null);
            //拥有应收款的合同id集合
            List<Long> hasPaymentCids = new ArrayList<>();
            Double sumAmount = 0D;
            for (PaasFormDataEntityExt entityExt : planList) {
                JSONObject data = entityExt.getData();
                Long contractId = data.getLongValue(PaymentEnum.CONTRACT.getAttr());
                hasPaymentCids.add(contractId);
                //已收或未收金额
                Double amount = FastJsonHelper.getDoubleOrDefaultFromFormData(data, paymentEnum.getAttr(), 0D);
                sumAmount = Arith.add(sumAmount, amount);
            }
            //移除所有有应收款的合同ids，剩余无应收款的合同ids
            dataIdList.removeAll(hasPaymentCids);
            if (dataIdList.size() > 0) {
                List<String> fieldList = Arrays.asList(ContractEnum.getAttrConnectData(ContractEnum.AMOUNT), ContractEnum.getAttrConnectData(ContractEnum.FINISH_AMOUNT), ContractEnum.getAttrConnectData(ContractEnum.UN_FINISH_AMOUNT));
                //这边改成从数据库去查询，因为es有延时
                List<ContractEntityExt> contractEntityExts = contractModel.findEntitysByIdIn(corpid, BasicConstant.ZERO, dataIdList);
                for (ContractEntityExt entityExt : contractEntityExts) {
                    //已收或未收金额
                    Double amount = FastJsonHelper.getDoubleOrDefaultFromFormData(entityExt.getData(), contractEnum.getAttr(), 0D);
                    sumAmount = Arith.add(sumAmount, amount);
                }
            }
            sheetAmountDetailGetVO.setSumAmount(sumAmount);
        }
        return sheetAmountDetailGetVO;
    }


    /**
     * 除了计算总的未收/已收金额，还要处理封装明细-按顺序封装明细内所有东西：签订人、金额、合同名、应收款名等
     * 注：最后封装的明细排序规则为：（1）第一层按照前端传入的合同id顺序（dataIdList的顺序）；（2）第二层内层按照应收款预计回款时间升序-红冲则直接按照原单的明细进行渲染
     * @param isRedFlag 是否红冲，如果红冲则读取关联原始回款单相关及其子单（红冲预收款不会关联合同和应收款，不会调用该接口）；否则读取应收款的未收款
     * @param sheetAmountDetailGetDTO 获取回款单明细dto
     * @param dataIdList 合同id集合
     * @param paymentEnum 未收/实收字段
     * @param contractEnum 未收/已收字段
     * @param corpid 公司id
     * @return SheetAmountDetailGetVO
     * @throws XbbException
     * @author zcp
     * @date 19/12/11 011 21:11
     * @update 19/12/11 011 21:11
     * @since v1.0
     * @version v1.0
     */
    private SheetAmountDetailGetVO getResult4AmountDetail(String corpid, boolean isRedFlag, SheetAmountDetailGetDTO sheetAmountDetailGetDTO, List<Long> dataIdList, PaymentEnum paymentEnum, ContractEnum contractEnum) throws XbbException {
        //封装查询应收款（未收或已收）的条件，如果是红冲的，只需要获得关联的原始回款单
        AmountDetailGetPojo amountDetailGetPojo = getPaymentAmountDetailGetPojo(isRedFlag, sheetAmountDetailGetDTO);
        SheetAmountDetailGetVO sheetAmountDetailGetVO = new SheetAmountDetailGetVO();
        //合同id-该合同的应收款/回款单list（把同一个合同的应收款/回款单放到一起）
        Map<Long, List<JSONObject>> resultMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        if (isRedFlag) {
            //创建红冲回款单，则只需读取到关联原始回款单，及其子单，然后明细通过其本身或子单进行渲染（只能红冲原单的已收）
            PaasFormDataEntityExt linkSheetEntity = amountDetailGetPojo.getLinkSheetEntity();
            //确保每个数据关联的合同、应收款都是一条（因为sheetList内只塞入子、及无子的回款单，而这类回款单都是只关联一个）
            List<PaasFormDataEntityExt> sheetList = new ArrayList<>();
            if (Objects.equals(linkSheetEntity.getAlone(), SheetAloneEnum.PARENT.getCode())) {
                //查询出所有子回款单
                sheetList.addAll(fundHelp.getChildEsDataListByParentUuid(IndexTypeEnum.IDX_SAAS_PAYMENT_SHEET, corpid, linkSheetEntity.getUuid(), null));
            } else {
                //无父子回款单则塞入本身
                sheetList.add(linkSheetEntity);
            }
            /**
             key：红冲回款单的合同id_应收款id
             value：红冲单据的金额
             */
            Map<String, Double> redCidPidAmountMap = saasPaymentSheetHelper.handleRedSheetsBySheet(corpid, sheetAmountDetailGetDTO.getLinkPreId(), amountDetailGetPojo);
            Set<String> belongUserSet = new HashSet<>();
            for (PaasFormDataEntityExt entityExt : sheetList) {
                JSONArray belongArr = FastJsonHelper.getJsonArrOrDefaultFromFormData(entityExt.getData(), PaymentSheetEnum.BELONG_ID.getAttr(), new JSONArray());
                if (belongArr.size() > 0) {
                    List<String> belongList = belongArr.toJavaList(String.class);
                    belongUserSet.addAll(belongList);
                }
            }
            //归属人userId-归属人姓名
            Map<String, String> belongNameMap = commonHelp.getUserNameMapByIds(corpid, belongUserSet);
            for (PaasFormDataEntityExt entityExt : sheetList) {
                JSONObject data = entityExt.getData();
                String paymentSheetType = FastJsonHelper.getStringFromFormData(data, PaymentSheetEnum.PAYMENT_SHEET_TYPE.getAttr());
                JSONArray idArr = FastJsonHelper.getJsonArrFromFormData(data, PaymentSheetEnum.CONTRACT.getAttr());
                if (idArr == null || idArr.isEmpty()) {
                    continue;
                }
                JSONArray contractNoArr = FastJsonHelper.getJsonArrOrDefaultFromFormData(data, PaymentSheetEnum.CONTRACT_LINK_TEXT.getAttr(), new JSONArray());
                //都是一条（因为sheetList内只塞入子、及无子的回款单，而这类回款单都是只关联一个）
                Long contractId = idArr.getLong(0);
                String contractSerialNo = (contractNoArr.size() <= 0) ? "" : contractNoArr.getString(0);
                JSONArray belongArr = FastJsonHelper.getJsonArrOrDefaultFromFormData(data, PaymentSheetEnum.BELONG_ID.getAttr(), new JSONArray());
                //红冲回款单直接用原单的归属人
                String belongUserId = (belongArr.size() <= 0)? "": belongArr.getString(0);
                String belongName = belongNameMap.getOrDefault(belongUserId, "");
                //应收款信息
                List<Long> pidList = FastJsonHelper.getJsonArrOrDefaultFromFormData(data, PaymentSheetEnum.PAYMENT.getAttr(), new JSONArray()).toJavaList(Long.class);
                List<IdNamePojo> paymentInfo = null;
                String key = contractId + "";
                if (pidList.size() > 0) {
                    Long paymentId = pidList.get(0);
                    key += "_" + paymentId;
                    JSONArray nameArr = FastJsonHelper.getJsonArrFromFormData(data, PaymentSheetEnum.PAYMENT_LINK_TEXT.getAttr());
                    String paymentSerialNo = nameArr.getString(0);
                    paymentInfo = Collections.singletonList(new IdNamePojo(paymentId, paymentSerialNo));
                }
                double sheetAmount = FundHelp.getSheetAmount4PaymentSheet(data, paymentSheetType);
                //关联了合同、应收款的明细红冲金额，此时为负值
                Double sheetRedAmount = redCidPidAmountMap.getOrDefault(key, 0D);
                Double left = Arith.add(sheetAmount, sheetRedAmount);
                if (left == 0) {
                    continue;
                }
                JSONObject jsonObject = sheetAmountDetailGetVO.formatInfo(contractId, contractSerialNo, belongUserId, belongName, paymentInfo, left, left);
                List<JSONObject> list = resultMap.getOrDefault(contractId, new ArrayList<>());
                list.add(jsonObject);
                resultMap.put(contractId, list);
            }
            //最后封装的明细排序规则为：按照前端传入的合同id顺序（dataIdList的顺序）
            List<JSONObject> list = new ArrayList<>();
            for (Long contractId : dataIdList) {
                if (resultMap.get(contractId) != null) {
                    list.addAll(resultMap.get(contractId));
                }
            }
            fundHelp.subRedAmount(XbbRefTypeEnum.PAYMENT_SHEET, sheetAmountDetailGetVO, amountDetailGetPojo);
            sheetAmountDetailGetVO.setList(list);
        } else {
            //非红冲，则读取应收款的未收
            //应收款排序，内部排序方法需要用到explainMap
            PaasFormExplainEntity paasFormExplainEntity = paasFormExplainModel.getByBusinessType4Distributor(RedundantTemplateTypeEnum.PAYMENT.getCode(), corpid, sheetAmountDetailGetDTO.getDistributorMark());
            if (Objects.isNull(paasFormExplainEntity)) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FORM_EXPLAIN_NOT_EXIST);
            }
            //排序字段：预计回款时间升序
            Map<String, String> sortMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            CompanyConfigEntity entity = companyConfigModel.getByConfigAlias(CompanyConfigEnum.WRITE_OFF_RULE.getAlias(), corpid);
            //FundSetDTO.sort 0：预计回款时间正序   1：预计回款时间倒序
            String sort = Objects.isNull(entity) || Objects.equals(entity.getConfigValue(), BasicConstant.ZERO_STRING) ? SortOrder.ASC.toString() : SortOrder.DESC.toString();
            sortMap.put("field", PaymentEnum.ESTIMATE_TIME.getAttr());
            sortMap.put("sort", sort);
            Map<String, FieldAttrEntity> explainMap = ExplainUtil.getExplainMap(paasFormExplainEntity.getExplains(), null);
            //得到应收款（未收/已收）查询结果：需对计划排序，因此需要explainMap、sortMap
            List<PaasFormDataEntityExt> planList = getPlanList4AmountDetailGet(amountDetailGetPojo, sheetAmountDetailGetDTO, explainMap, sortMap);
            //合同id-签订人userId
            Map<Long, String> cidSignPersonMap = new HashMap<>(dataIdList.size());
            //合同id-合同信息
            Map<Long, PaasFormDataEntityExt> cidMap = new HashMap<>(dataIdList.size());
            //签订人userId-签订人姓名
            Map<String, String> signNameMap = handleContractInfo(corpid, dataIdList, cidSignPersonMap, cidMap);
            String attrPlan = paymentEnum.getAttr();
            String attrContract = contractEnum.getAttr();
            String attrContract4Plan = PaymentEnum.CONTRACT.getAttr();
            //封装最终结果：（1）第一层按照前端传入的采购合同id顺序（dataIdList的顺序）；（2）第二层内层按照付款计划预计付款时间升序-红冲则直接按照原单的明细进行渲染
            fundHelp.formatDetailResult(true, sheetAmountDetailGetVO, dataIdList, resultMap, planList, cidMap, cidSignPersonMap, signNameMap, attrPlan, attrContract, attrContract4Plan);
        }
        return sheetAmountDetailGetVO;
    }

    /**
     * 查询合同信息，并处理构建合同id和合同一些信息的对应关系map，以及签订人userId和签订人名称对应关系map
     * @param corpid 公司id
     * @param dataIdList 合同id集合
     * @param cidSignPersonMap 合同id-签订人userId
     * @param cidMap 合同id-合同信息
     * @return java.util.Map<java.lang.String,java.lang.String>
     * @throws XbbException
     * @author zcp
     * @date 19/11/19 019 16:25
     * @update 19/11/19 019 16:25
     * @since v1.0
     * @version v1.0
     */
    private Map<String, String> handleContractInfo(String corpid, List<Long> dataIdList, Map<Long, String> cidSignPersonMap, Map<Long, PaasFormDataEntityExt> cidMap) throws XbbException {
        List<String> fieldList = Arrays.asList("id", ContractEnum.CONTRACT_NO.getAttr(), ContractEnum.getAttrConnectData(ContractEnum.AMOUNT), ContractEnum.getAttrConnectData(ContractEnum.FINISH_AMOUNT), ContractEnum.getAttrConnectData(ContractEnum.UN_FINISH_AMOUNT), ContractEnum.getAttrConnectData(ContractEnum.SIGN_PERSON));
        List<PaasFormDataEntityExt> contracts = formHelp.getFormListByIds4All(corpid, IndexTypeEnum.IDX_SAAS_CONTRACT, dataIdList, fieldList);
        Set<String> signUserIds = new HashSet<>();
        for (PaasFormDataEntityExt entityExt : contracts) {
            String signUserId = FastJsonHelper.getStringOrDefaultFromFormData(entityExt.getData(), ContractEnum.SIGN_PERSON.getAttr(), "-1");
            signUserIds.add(signUserId);
            cidSignPersonMap.put(entityExt.getId(), signUserId);
            cidMap.put(entityExt.getId(), entityExt);
        }
        //签订人userId-签订人姓名
        return commonHelp.getUserNameMapByIds(corpid, signUserIds);
    }

    /**
     * 非红冲：则封装查询应收款（未收或已收）的条件
     * 红冲：还需要获得关联的原始回款单
     * @param isRedFlag 是否红冲，如果红冲则读取关联原始回款单相关（红冲预收款不会关联合同和应收款，不会调用该接口）；否则读取应收款的未收款
     * @param sheetAmountDetailGetDTO 获取回款单明细dto
     * @return AmountDetailGetPojo
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private AmountDetailGetPojo getPaymentAmountDetailGetPojo(boolean isRedFlag, SheetAmountDetailGetDTO sheetAmountDetailGetDTO) throws XbbException {
        AmountDetailGetPojo amountDetailGetPojo = new AmountDetailGetPojo();
        List<ConditionsEntityExt> conditionList = new ArrayList<>();
        //计划状态处理（如果是红冲，还需要读取原单、及原单已红冲金额，用于计算原单可红冲金额）
        ConditionsEntityExt planStatus;
        String corpid = sheetAmountDetailGetDTO.getCorpid();
        if (isRedFlag) {
            //红冲回款单，则需要传入关联原始回款单（红冲预收款余额不需要关联原单，且也不会调用该接口
            Long linkPreId = sheetAmountDetailGetDTO.getLinkPreId();
            if (Objects.equals(linkPreId, null) || Objects.equals(linkPreId, BasicConstant.ZERO_LONG)) {
                throw new XbbException(PaymentErrorCodeEnum.API_ERROR_208040, PaymentErrorCodeEnum.API_ERROR_208040.getMsg(), XbbRefTypeEnum.PAYMENT_SHEET.getName());
            }
            //关联的原始回款单
            PaymentSheetEntityExt linkSheetEntity = paymentSheetModel.getByKey(linkPreId, corpid);
            if (Objects.isNull(linkSheetEntity) || Objects.equals(linkSheetEntity.getDel(), BasicConstant.ONE)) {
                throw new XbbException(PaymentErrorCodeEnum.API_ERROR_208045, PaymentErrorCodeEnum.API_ERROR_208045.getMsg(), XbbRefTypeEnum.PAYMENT_SHEET.getName());
            }
            amountDetailGetPojo.setLinkSheetEntity(linkSheetEntity);
            //读取已收、部分收款
            planStatus = new ConditionsEntityExt(PaymentEnum.STATUS.getAttr(), "", PaymentEnum.STATUS.getFieldType(), ConditionEnum.IN.getSymbol(), Arrays.asList(PaymentStatusEnum.RECEIVABLES.getCode(), PaymentStatusEnum.PARTIAL_RECEIPTS.getCode()));
        } else {
            //读取未收款、部分收款
            planStatus = new ConditionsEntityExt(PaymentEnum.STATUS.getAttr(), "", PaymentEnum.STATUS.getFieldType(), ConditionEnum.IN.getSymbol(), Arrays.asList(PaymentStatusEnum.UN_RECEIVABLES.getCode(), PaymentStatusEnum.PARTIAL_RECEIPTS.getCode()));
            //有未收金额的应收款，如果应收款的未收金额为0，则不能选择到应收款。因为创建回款单时要把应收款的未收款减少
            conditionList.add(new ConditionsEntityExt(PaymentEnum.UN_AMOUNT.getAttr(), "", PaymentEnum.UN_AMOUNT.getFieldType(), ConditionEnum.GREATERTHAN.getSymbol(), Collections.singletonList(BasicConstant.ZERO_DOUBLE)));
        }
        conditionList.add(planStatus);
        //塞入合同ids条件
        List<Object> contractIdList = new ArrayList<>(sheetAmountDetailGetDTO.getDataIdList());
        ConditionsEntityExt conditionsEntityExt = new ConditionsEntityExt(PaymentEnum.CONTRACT.getAttr(), "", PaymentEnum.CONTRACT.getFieldType(), ConditionEnum.IN.getSymbol(), contractIdList);
        conditionList.add(conditionsEntityExt);
        //paymentIdList为空，则返回合同关联的所有应收款（无计划则返回合同本身）；该值不为空，则只展示该paymentIdList内的应收款
        List<Long> paymentIdList = sheetAmountDetailGetDTO.getPaymentIdList();
        if (sheetAmountDetailGetDTO.getEmptyFilterFlag()) {
            if (paymentIdList == null) {
                paymentIdList = new ArrayList<>();
            }
            paymentIdList.add(-1L);
        }
        if (CollectionUtils.isNotEmpty(paymentIdList)) {
            ConditionsEntityExt paymentIdConditions = new ConditionsEntityExt(BasicConstant.ID, "", FieldTypeEnum.DATAID.getType(), ConditionEnum.IN.getSymbol(), new ArrayList<>(paymentIdList));
            conditionList.add(paymentIdConditions);
        }
        amountDetailGetPojo.setConditionList(conditionList);
        return amountDetailGetPojo;
    }

    /**
     * 根据关联id和条件获取数据
     * @param amountDetailGetPojo 获取金额明细时需要用到的条件、原单信息pojo
     * @param sheetAmountDetailGetDTO dto
     * @param explainMap 解释map（排序时用到）
     * @param sortMap 排序map
     * @return AmountDetailGetPojo
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private List<PaasFormDataEntityExt> getPlanList4AmountDetailGet(AmountDetailGetPojo amountDetailGetPojo, SheetAmountDetailGetDTO sheetAmountDetailGetDTO, Map<String, FieldAttrEntity> explainMap, Map<String, String> sortMap) throws XbbException {
        //查询出部分字段（其他id、dataId等字段内部会强制加上去，无需重复置入）
        List<String> fieldList = Arrays.asList(PaymentEnum.CONTRACT.getAttr(), PaymentEnum.UN_AMOUNT.getAttr(), REAL_AMOUNT.getAttr());
        FormDataListDTO formDataListDTO = new FormDataListDTO();
        BeanUtil.copyProperties(sheetAmountDetailGetDTO, formDataListDTO);
        formDataListDTO.setAppId(null);
        formDataListDTO.setFormId(null);
        formDataListDTO.setDel(DelEnum.NORMAL.getDel());
        formDataListDTO.setConditions(amountDetailGetPojo.getConditionList());
        formDataListDTO.setBusinessType(XbbRefTypeEnum.PAYMENT.getCode());
        formDataListDTO.setSortMap(sortMap);
        formDataListDTO.setExplainMap(explainMap);
        formDataListDTO.setFieldList(fieldList);
        formDataListDTO.setSearchSubForm(false);
        PaasFormDataEsListVO paasFormDataEsListVO = paasFormDataEsModel.list(formDataListDTO, IndexTypeEnum.IDX_SAAS_PAYMENT);
        PageHelper pageHelper = paasFormDataEsListVO.getPageHelper();
        if (pageHelper.getRowsCount() > SizeConstant.PAGE_SIZE) {
            //查询合同下的应收款时，限制只能查询1w条
            throw new XbbException(PaymentErrorCodeEnum.API_ERROR_208030, PaymentErrorCodeEnum.API_ERROR_208030.getMsg(), SizeConstant.PAGE_SIZE);
        }
        return paasFormDataEsListVO.getPaasFormDataESList();
    }

    /**
     * 校验已收金额是否已经超过应收款的未收金额
     *
     * @param paymentSheetTypeEnum
     * @param dataList 回款单里面的data
     * @param corpid
     * @param paymentIdToAmount id和金额的对应关系map
     * @param paymentIds 应收id列表
     */
    public void checkPayment(SheetTypeEnum paymentSheetTypeEnum, JSONObject dataList, String corpid, Map<Long, Double> paymentIdToAmount, Set<Long> paymentIds) throws XbbException {
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("idIn", paymentIds);
        param.put("corpid", corpid);
        List<PaymentEntityExt> payments = paymentModel.findEntitys(param);
        if (Objects.isNull(payments)) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FORM_DATA_NOT_EXIST);
        }
        boolean isRedFlag = PaymentSheetTypeEnum.isRed(paymentSheetTypeEnum.getCode());
        boolean isBadFlag = PaymentSheetTypeEnum.isBad(paymentSheetTypeEnum.getCode());
        for (PaymentEntityExt paymentEntityExt : payments) {
            String name = paymentEntityExt.getSerialNo();
            if (Objects.equals(paymentEntityExt.getDel(), DelEnum.DELETE.getDel())) {
                throw new XbbException(PaymentErrorCodeEnum.API_ERROR_208032, PaymentErrorCodeEnum.API_ERROR_208032.getMsg(), XbbRefTypeEnum.PAYMENT.getName(), name);
            }
            Long paymentId = paymentEntityExt.getId();
            //当前应收款的实收金额(新增)
            Double value = paymentIdToAmount.getOrDefault(paymentId, 0D);
            if (value == 0) {
                continue;
            }
            JSONObject data = paymentEntityExt.getData();
            Double tempAmount = value;
            Double planRealAmount = FastJsonHelper.getDoubleOrDefaultFromFormData(data, REAL_AMOUNT.getAttr(), 0D);
            Double unAmount = FastJsonHelper.getDoubleOrDefaultFromFormData(data, PaymentEnum.UN_AMOUNT.getAttr(), 0D);
            if (isRedFlag) {
                if (Arith.sub(tempAmount , planRealAmount) > ConfigConstant.amountAccuracy) {
                    //红冲，限制红冲金额小于等于回款金额
                    throw new XbbException(SystemErrorCodeEnum.API_ERROR_100028, ErrorMessageConstant.DETAIL_BEYOND_AMOUNT_RED, XbbRefTypeEnum.PAYMENT.getName() + name);
                }
            }else {
                //普通回款（已核销、预收款核销、坏账），限制回款金额小于等于未收金额
                if (Arith.sub(value , unAmount) > ConfigConstant.amountAccuracy) {
                    if (isBadFlag) {
                        throw new XbbException(SystemErrorCodeEnum.API_ERROR_100028, ErrorMessageConstant.DETAIL_BEYOND_AMOUNT_BAD, XbbRefTypeEnum.PAYMENT.getName(), name);
                    }
                    throw new XbbException(SystemErrorCodeEnum.API_ERROR_100028, ErrorMessageConstant.DETAIL_BEYOND_AMOUNT, XbbRefTypeEnum.PAYMENT.getName(), name);
                }
            }
        }
    }

    /**
     * 新增回款单之后，需要返回与回款单关联的应收款信息
     *
     * @param paymentSheetTypeEnum
     * @param sheetData
     * @param corpid
     * @param paymentIdToAmount
     * @param paymentIds
     * @return
     * @throws XbbException
     */
    public HandleLinkInfoVO handlePayment(PaymentSheetTypeEnum paymentSheetTypeEnum, JSONObject sheetData, String corpid, Map<Long, Double> paymentIdToAmount, Set<Long> paymentIds) throws XbbException {
        Long sheetPaymentTime = sheetData.getLong(PaymentSheetEnum.PAYMENT_TIME.getAttr());
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put("idIn", paymentIds);
        param.put("corpid", corpid);
        List<PaymentEntityExt> payments = paymentModel.findEntitys(param);
        if (Objects.isNull(payments)) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FORM_DATA_NOT_EXIST);
        }
        List<UpdateDataEntity> updateList = new ArrayList<>();
        Map<Long, Long> idModelTypeMap = new HashMap<>(payments.size());
        boolean isRedFlag = PaymentSheetTypeEnum.isRed(paymentSheetTypeEnum.getCode());
        boolean isBadFlag = PaymentSheetTypeEnum.isBad(paymentSheetTypeEnum.getCode());
        for (PaymentEntityExt paymentEntityExt : payments) {
            Long paymentId = paymentEntityExt.getId();
            //当前应收款的实收金额(新增)
            Double value = paymentIdToAmount.getOrDefault(paymentId, 0D);
            if (value == 0) {
                continue;
            }
            JSONObject data = paymentEntityExt.getData();
            Double planRealAmount = FastJsonHelper.getDoubleOrDefaultFromFormData(data, REAL_AMOUNT.getAttr(), 0D);
            Double unAmount = FastJsonHelper.getDoubleOrDefaultFromFormData(data, PaymentEnum.UN_AMOUNT.getAttr(), 0D);
            //红冲应收金额
            Double redAmount = FastJsonHelper.getDoubleOrDefaultFromFormData(data, PaymentEnum.RED_AMOUNT.getAttr(), 0D);
            JSONObject updateData = new JSONObject();
            if (isRedFlag) {
                //红冲的，把金额置为负数。用tempAmount计算金额是否合法
                value = Arith.mul(value, -1);
                updateData.put(PaymentEnum.HAS_RED.getAttr(), HasRedEnum.HAS_RED.getCode());
            } else {
                if (!isBadFlag) {
                    //坏账，红冲不需要置应收款的实际收款日期，只有已核销、预收款核销才需要
                    Long realPayTime = data.getLongValue(PaymentEnum.REAL_PAY_TIME.getAttr());
                    if (realPayTime < sheetPaymentTime) {
                        updateData.put(PaymentEnum.REAL_PAY_TIME.getAttr(), sheetPaymentTime);
                    }
                }
            }
            //处理应收款的已收、未收、回款状态
            Double planAmount = FastJsonHelper.getDoubleOrDefaultFromFormData(data, PaymentEnum.AMOUNT.getAttr(), 0D);
            Double badAmount = FastJsonHelper.getDoubleOrDefaultFromFormData(data, PaymentEnum.BAD_AMOUNT.getAttr(), 0D);
            handlePlanAmountStatus4Add(isBadFlag, value, updateData, planAmount, planRealAmount, unAmount, badAmount, redAmount);
            UpdateDataEntity updateDataEntity = ExplainUtil.getUpdateData(paymentId, updateData, corpid);
            updateList.add(updateDataEntity);
            idModelTypeMap.put(paymentId, paymentEntityExt.getSysLong10());
        }
        return new HandleLinkInfoVO(updateList, idModelTypeMap);
    }

    /**
     * 处理应收款的已收、未收、回款状态：回款金额增加的情况（已核销、预收款核销、红冲-需确保金额为负数）
     * 注：坏账的优先级最高，如果有坏账金额，则回款状态要么是坏账、要么是部分坏账
     * @param isBadFlag true坏账，false非坏账。当是坏账时，应收款的状态直接置为坏账
     * @param value 新传入的回款金额
     * @param updateData 要更新的data
     * @param planAmount 计划内的回款金额
     * @param planRealAmount 计划内的已收金额
     * @param unAmount 计划内的未收金额
     * @param badAmount 坏账金额
     * @param redAmount 红冲应收金额
     * @author zcp
     * @date 19/11/23 023 15:22
     * @update 19/11/23 023 15:22
     * @since v1.0
     * @version v1.0
     */
    private void handlePlanAmountStatus4Add(boolean isBadFlag, Double value, JSONObject updateData, Double planAmount, Double planRealAmount, Double unAmount, Double badAmount, Double redAmount) {
        //未收减去value，如果value为负数(红冲回款单)，则为加
        unAmount = Arith.sub(unAmount, value);
        if (isBadFlag) {
            //原坏账金额增加上新的坏账金额(其实不加也没问题，因为坏账只能一次性全部坏完，不存在再坏账一次)
            badAmount = Arith.add(badAmount, value);
            updateData.put(PaymentEnum.BAD_AMOUNT.getAttr(), value);
        } else {
            //已收加上amount（坏账不需要加实收：坏账是坏的未收，只对未收金额减去坏账金额即可）
            value = Arith.add(planRealAmount, value);
            updateData.put(REAL_AMOUNT.getAttr(), value);
        }
        String planStatus = payAndPaymentSheetHelp.handleStatus(planAmount, unAmount, badAmount, redAmount);
        updateData.put(PaymentEnum.STATUS.getAttr(), planStatus);
        updateData.put(PaymentEnum.UN_AMOUNT.getAttr(), unAmount);
    }

}
